﻿using System;
using System.Collections.Generic;
using GeoAPI.Geometries;

namespace NetTopologySuite.Geometries.Implementation
{
	[Serializable]
	public class DotSpatialAffineCoordinateSequence : ICoordinateSequence, ICloneable
	{
		public DotSpatialAffineCoordinateSequence(IList<Coordinate> coordinates, Ordinates ordinates)
		{
			this._ordinates = ordinates;
			if (coordinates == null)
			{
				this._xy = new double[0];
				return;
			}
			this._xy = new double[2 * coordinates.Count];
			int num = 0;
			for (int i = 0; i < coordinates.Count; i++)
			{
				this.XY[num++] = coordinates[i].X;
				this.XY[num++] = coordinates[i].Y;
			}
			if ((ordinates & Ordinates.Z) == Ordinates.Z)
			{
				this._z = new double[coordinates.Count];
				for (int j = 0; j < coordinates.Count; j++)
				{
					this.Z[j] = coordinates[j].Z;
				}
			}
			if ((ordinates & Ordinates.M) == Ordinates.M)
			{
				this._m = new double[coordinates.Count];
				for (int k = 0; k < coordinates.Count; k++)
				{
					this.M[k] = double.NaN;
				}
			}
			this._ordinates = ordinates;
		}

		public DotSpatialAffineCoordinateSequence(int size, int dimension)
		{
			this._xy = new double[2 * size];
			if (dimension <= 2)
			{
				return;
			}
			this._z = new double[size];
			for (int i = 0; i < size; i++)
			{
				this._z[i] = double.NaN;
			}
			this._m = new double[size];
			for (int j = 0; j < size; j++)
			{
				this._m[j] = double.NaN;
			}
		}

		public DotSpatialAffineCoordinateSequence(int size, Ordinates ordinates)
		{
			this._xy = new double[2 * size];
			this._ordinates = ordinates;
			if ((ordinates & Ordinates.Z) == Ordinates.Z)
			{
				this._z = new double[size];
				for (int i = 0; i < size; i++)
				{
					this._z[i] = double.NaN;
				}
			}
			if ((ordinates & Ordinates.M) == Ordinates.M)
			{
				this._m = new double[size];
				for (int j = 0; j < size; j++)
				{
					this._m[j] = double.NaN;
				}
			}
		}

		public DotSpatialAffineCoordinateSequence(ICoordinateSequence coordSeq, Ordinates ordinates)
		{
			int count = coordSeq.Count;
			this._xy = new double[2 * count];
			if ((ordinates & Ordinates.Z) == Ordinates.Z)
			{
				this._z = new double[count];
			}
			if ((ordinates & Ordinates.M) == Ordinates.M)
			{
				this._m = new double[count];
			}
			DotSpatialAffineCoordinateSequence dotSpatialAffineCoordinateSequence = coordSeq as DotSpatialAffineCoordinateSequence;
			if (dotSpatialAffineCoordinateSequence != null)
			{
				double[] array = null;
				Buffer.BlockCopy(dotSpatialAffineCoordinateSequence._xy, 0, this._xy, 0, 16 * count);
				if ((ordinates & Ordinates.Z) == Ordinates.Z)
				{
					Buffer.BlockCopy((dotSpatialAffineCoordinateSequence.Z != null) ? dotSpatialAffineCoordinateSequence.Z : (array = DotSpatialAffineCoordinateSequence.NullOrdinateArray(count)), 0, this._z, 0, 8 * count);
				}
				if ((ordinates & Ordinates.M) == Ordinates.M)
				{
					Buffer.BlockCopy((dotSpatialAffineCoordinateSequence.M != null) ? dotSpatialAffineCoordinateSequence.M : (array ?? DotSpatialAffineCoordinateSequence.NullOrdinateArray(count)), 0, this._m, 0, 8 * count);
				}
				this._ordinates = ordinates;
				return;
			}
			int num = 0;
			for (int i = 0; i < coordSeq.Count; i++)
			{
				this._xy[num++] = coordSeq.GetX(i);
				this._xy[num++] = coordSeq.GetY(i);
				if (this._z != null)
				{
					this._z[i] = coordSeq.GetOrdinate(i, Ordinate.Z);
				}
				if (this._m != null)
				{
					this._m[i] = coordSeq.GetOrdinate(i, Ordinate.M);
				}
			}
		}

		private static double[] NullOrdinateArray(int size)
		{
			double[] array = new double[size];
			for (int i = 0; i < size; i++)
			{
				array[i] = double.NaN;
			}
			return array;
		}

		public DotSpatialAffineCoordinateSequence(double[] xy, double[] z)
		{
			this._xy = xy;
			this._z = z;
			this._ordinates = Ordinates.XY;
			if (this._z != null)
			{
				this._ordinates |= Ordinates.Z;
			}
		}

		public DotSpatialAffineCoordinateSequence(double[] xy, double[] z, double[] m) : this(xy, z)
		{
			this._m = m;
			this._ordinates = Ordinates.XYZM;
		}

		public object Clone()
		{
			return new DotSpatialAffineCoordinateSequence(this, this.Ordinates);
		}

		public Coordinate GetCoordinate(int i)
		{
			int num = 2 * i;
			if (this._z != null)
			{
				return new Coordinate(this._xy[num++], this._xy[num], this._z[i]);
			}
			return new Coordinate(this._xy[num++], this._xy[num]);
		}

		public Coordinate GetCoordinateCopy(int i)
		{
			return this.GetCoordinate(i);
		}

		public void GetCoordinate(int index, Coordinate coord)
		{
			coord.X = this._xy[2 * index];
			coord.Y = this._xy[2 * index + 1];
			coord.Z = ((this._z != null) ? this._z[index] : double.NaN);
		}

		public double GetX(int index)
		{
			return this._xy[2 * index];
		}

		public double GetY(int index)
		{
			return this._xy[2 * index + 1];
		}

		public double GetOrdinate(int index, Ordinate ordinate)
		{
			switch (ordinate)
			{
			case Ordinate.X:
				return this._xy[index * 2];
			case Ordinate.Y:
				return this._xy[index * 2 + 1];
			case Ordinate.Z:
				if (this._z == null)
				{
					return double.NaN;
				}
				return this._z[index];
			case Ordinate.M:
				if (this._m == null)
				{
					return double.NaN;
				}
				return this._m[index];
			default:
				throw new NotSupportedException();
			}
		}

		public void SetOrdinate(int index, Ordinate ordinate, double value)
		{
			switch (ordinate)
			{
			case Ordinate.X:
				this._xy[index * 2] = value;
				break;
			case Ordinate.Y:
				this._xy[index * 2 + 1] = value;
				break;
			case Ordinate.Z:
				if (this._z != null)
				{
					this._z[index] = value;
				}
				break;
			case Ordinate.M:
				if (this._m != null)
				{
					this._m[index] = value;
				}
				break;
			default:
				throw new NotSupportedException();
			}
			this._coordinateArrayRef = null;
		}

		private Coordinate[] GetCachedCoords()
		{
			if (this._coordinateArrayRef == null || !this._coordinateArrayRef.IsAlive)
			{
				return null;
			}
			Coordinate[] array = (Coordinate[])this._coordinateArrayRef.Target;
			if (array != null)
			{
				return array;
			}
			this._coordinateArrayRef = null;
			return null;
		}

		public Coordinate[] ToCoordinateArray()
		{
			Coordinate[] array = this.GetCachedCoords();
			if (array != null)
			{
				return array;
			}
			int num = 0;
			int count = this.Count;
			array = new Coordinate[count];
			if (this._z != null)
			{
				for (int i = 0; i < count; i++)
				{
					array[i] = new Coordinate(this._xy[num++], this._xy[num++], this._z[i]);
				}
			}
			else
			{
				for (int j = 0; j < count; j++)
				{
					array[j] = new Coordinate(this._xy[num++], this._xy[num++]);
				}
			}
			this._coordinateArrayRef = new WeakReference(array);
			return array;
		}

		public Envelope ExpandEnvelope(Envelope env)
		{
			int num = 0;
			for (int i = 0; i < this.Count; i++)
			{
				env.ExpandToInclude(this._xy[num++], this._xy[num++]);
			}
			return env;
		}

		public ICoordinateSequence Reversed()
		{
			double[] array = new double[this._xy.Length];
			double[] array2 = null;
			double[] array3 = null;
			if (this._z != null)
			{
				array2 = new double[this._z.Length];
			}
			if (this._m != null)
			{
				array3 = new double[this._m.Length];
			}
			int num = 2 * this.Count;
			int num2 = this.Count;
			for (int i = 0; i < this.Count; i++)
			{
				array[--num] = this._xy[2 * i + 1];
				array[--num] = this._xy[2 * i];
				num2--;
				if (this._z != null)
				{
					array2[num2] = this._z[i];
				}
				if (this._m != null)
				{
					array3[num2] = this._m[i];
				}
			}
			return new DotSpatialAffineCoordinateSequence(array, array2, array3);
		}

		public int Dimension
		{
			get
			{
				int num = 2;
				if (this._z != null)
				{
					num++;
				}
				if (this._m != null)
				{
					num++;
				}
				return num;
			}
		}

		public Ordinates Ordinates
		{
			get
			{
				return this._ordinates;
			}
		}

		public int Count
		{
			get
			{
				return this.XY.Length / 2;
			}
		}

		public double[] XY
		{
			get
			{
				return this._xy;
			}
		}

		public double[] Z
		{
			get
			{
				return this._z;
			}
		}

		public double[] M
		{
			get
			{
				return this._m;
			}
		}

		public void ReleaseCoordinateArray()
		{
			this._coordinateArrayRef = null;
		}

		private readonly double[] _xy;

		private readonly double[] _z;

		private readonly double[] _m;

		private readonly Ordinates _ordinates;

		[NonSerialized]
		private WeakReference _coordinateArrayRef;
	}
}
